#include <vector>
#include <string>
#include <iostream>
#include <cassert>
#include <opencv2/opencv.hpp>
#include <opencv2/core/utils/logger.hpp>
#include <cstring>
#include "handler.h"
#include "step.h"
#include "app.h"

using namespace std;
using namespace cv;
using namespace YQ;


namespace Global {
static const string test_resource_dir = "./test_resource/";
static const string face_dir = test_resource_dir + "face/";
static const string not_face_dir = test_resource_dir + "not_face/";
static const string similar_face_dir = test_resource_dir + "similar_face/";

static const vector<string> test_face_img = {
	face_dir + "face_1.jpg",
	face_dir + "face_2.jpg",
	face_dir + "face_3.jpg",
	face_dir + "face_4.jpg",
	face_dir + "face_5.jpg",
	face_dir + "face_6.jpg",
	face_dir + "face_7.jpg",
	face_dir + "face_8.jpg",
	face_dir + "face_9.jpg",
	face_dir + "face_10.jpg"
};

static const vector<string> test_not_face_img = {
	not_face_dir + "not_face_1.jpg",
	not_face_dir + "not_face_2.jpg",
	not_face_dir + "not_face_3.jpg",
	not_face_dir + "not_face_4.jpg",
	not_face_dir + "not_face_5.jpg",
	not_face_dir + "not_face_6.jpg",
	not_face_dir + "not_face_7.jpg",
	not_face_dir + "not_face_8.jpg",
	not_face_dir + "not_face_9.jpg",
	not_face_dir + "not_face_10.jpg"
};

static const vector<string>similar_face_img = {
	similar_face_dir + "yq_1.jpg",
	similar_face_dir + "yq_2.jpg",
	similar_face_dir + "yq_3.jpg",
	similar_face_dir + "yq_4.jpg"
};

}

void
__test__ (void (*testFunc) (), string msg);
#define TEST(func) __test__(func, #func)

void test_step_toImageData_toMat();
void test_step_toVector();
void test_step_isFaceExist();
void test_step_getBiggestFace();
void test_step_getBiggestFaceRect();
void test_step_getBiggestFaceFeatures();
void test_step_getFacePoints();
void test_step_getFaceFeatures();
void test_app_getBiggestFaceSimilarity();

void
test_step()
{
	TEST (test_step_toImageData_toMat);
	TEST (test_step_toVector);
	TEST (test_step_isFaceExist);
	TEST (test_step_getBiggestFace);
	TEST (test_step_getBiggestFaceRect);
	TEST (test_step_getBiggestFaceFeatures);
	TEST (test_step_getFacePoints);
	TEST (test_step_getFaceFeatures);
	TEST (test_app_getBiggestFaceSimilarity);
}

void
test_video()
{
	vector<int> id = getAvailableCameraId();
	VideoCapture camera = getDefaultCamera();
	cout << getCameraSize (camera).width << "," << getCameraSize (camera).height << endl;
	string window = "test_video";
	namedWindow (window);
	
	constexpr int fps = 60;
	for (; ;) {
		Mat img = getAnImageFromVideo (camera);
		imshow (window, img);
		if (waitKey (1000 / fps) == 27)
			break;
	}
	camera.release();
}

using seeta::FaceDetector;
int
main (void)
{
	// test_step();
	test_video();
	cout << "测试全部通过" << endl;
}

void
__test__ (void (*testFunc) (), string msg)
{
	cout << "开始测试\t" << msg << endl;
	testFunc();
}

void
test_step_toImageData_toMat()
{
	for (string file : Global::test_face_img) {
		Mat cv_img = imread (file, IMREAD_UNCHANGED);
		ImageData seeta_img_1  = toImageData (file);
		ImageData seeta_img_2 = toImageData (cv_img);
		vector<ImageData> seeta_imgs{seeta_img_1, seeta_img_2};
		for (ImageData seeta_img : seeta_imgs) {
			//正转换测试（ImageData --> Mat）
			assert (cv_img.rows == seeta_img.height &&
			        cv_img.cols == seeta_img.width &&
			        cv_img.channels() == seeta_img.channels
			       );
			//逆转换测试（Mat --> ImageData）
			Mat back = toMat (seeta_img);
			assert (cv_img.rows == back.rows &&
			        cv_img.cols == back.cols &&
			        cv_img.channels() == back.channels());
		}
	}
}

void
test_step_toVector()
{
	shared_ptr<FaceDetector> detector = makeDefaultHandler<FaceDetector>();
	for (string i : Global::test_face_img) {
		ImageData img = toImageData (i);
		SeetaFaceInfoArray faces_info = detector->detect (img);
		vector<SeetaFaceInfo> faces_info_vector = toVector (faces_info);
		assert (faces_info.size > 0 &&
		        static_cast<int> (faces_info_vector.size()) == faces_info.size);
	}
}

void
test_step_isFaceExist()
{
	for (string i : Global::test_face_img) {
		FaceExtractor face (i);
		assert (face.isFaceExist());
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		assert (!face.isFaceExist());
	}
}

void
test_step_getBiggestFace()
{
	for (string i : Global::test_face_img) {
		FaceExtractor face (i);
		face.getBiggestFace();
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		try {
			face.getBiggestFace();
		} catch (const runtime_error &e) {
		}
	}
}

void
test_step_getBiggestFaceRect()
{
	for (string i : Global::test_face_img) {
		FaceExtractor face (i);
		face.getBiggestFaceRect();
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		try {
			face.getBiggestFaceRect();
		} catch (const runtime_error &e) {
		}
	}
}

void
test_step_getBiggestFaceFeatures()
{
	for (string i : Global::test_face_img) {
		FaceExtractor face (i);
		face.getBiggestFaceFeatures();
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		try {
			face.getBiggestFaceFeatures();
		} catch (const runtime_error &e) {
		}
	}
}

void
test_step_getFacePoints()
{
	for (string i : Global::test_face_img) {
		FaceExtractor face (i);
		face.getFacePoints (face.getBiggestFaceRect());
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		try {
			face.getFacePoints (face.getBiggestFaceRect());
		} catch (const runtime_error &e) {
		}
	}
}

void
test_step_getFaceFeatures()
{
	for (string i : Global::test_face_img) {
		FaceExtractor  face (i);
		face.getFaceFeatures (face.getFacePoints (face.getBiggestFaceRect()));
	}
	for (string i : Global::test_not_face_img) {
		FaceExtractor face (i);
		try {
			face.getFaceFeatures (face.getFacePoints (face.getBiggestFaceRect()));
		} catch (const runtime_error &e) {
		}
	}
}

void
test_app_getBiggestFaceSimilarity()
{
	for (string i : Global::similar_face_img) {
		ImageData face_1 = toImageData (i);
		for (string j : Global::similar_face_img) {
			ImageData face_2 = toImageData (j);
			printf ("\t%s 与 %s的相似度为: %f\n",
			        strrchr (i.c_str(), '/') + 1,
			        strrchr (j.c_str(), '/') + 1,
			        getBiggestFaceSimilarity (face_1, face_2)
			       );
		}
		printf ("\n");
	}
}